#include "EpilPrimaryExpressionParser.h"
#include "EpilPrefixExpressionParser.h"
#include "EpilFieldParser.h"
#include "EpilFieldExpressionCode.h"
#include "EpilIndexParser.h"
#include "EpilIndexExpressionCode.h"
#include "EpilParserException.h"
#include "EpilFunctionArgumentsParser.h"
#include "EpilMethodCallExpressionCode.h"
#include "EpilFunctionCallExpressionCode.h"
#include "EpilPointerHelper.h"


EpilPrimaryExpressionParser::EpilPrimaryExpressionParser( EpilTokenStream & tokens )
: EpilSyntaxParserBase( tokens )
{
}


void EpilPrimaryExpressionParser::Parse()
{
	// primaryexp -> prefixexp { `.' NAME | `[' exp `]' | `:' NAME funcargs | funcargs }

	EpilPrefixExpressionParser prefixExprParser( this->GetTokens() );
	prefixExprParser.Parse();
	m_expression = prefixExprParser.GetExpression();

	for(;;)
	{
		switch( this->GetTokens().GetCurrentToken().type )
		{
		case ETT_OpTableMemberAccess:
			{
				EpilFieldExpressionCode * field = new EpilFieldExpressionCode( this->GetTokens().GetCurrentToken().location );
				EpilFieldParser fieldParser( this->GetTokens() );
				try
				{
					fieldParser.Parse();
				}
				catch( ... )
				{
					EpilPointerHelper::SafeDelete( m_expression );
					throw;
				}
				field->SetTableExpression( m_expression );
				field->SetFieldName( fieldParser.GetFieldName() );
				m_expression = field;
			}
			break;
		case ETT_OpenBracket:
			{
				EpilIndexExpressionCode * index = new EpilIndexExpressionCode( this->GetTokens().GetCurrentToken().location );
				EpilIndexParser indexParser( this->GetTokens() );
				try
				{
					indexParser.Parse();
				}
				catch( ... )
				{
					EpilPointerHelper::SafeDelete( m_expression );
					throw;
				}
				index->SetTableExpression( m_expression );
				index->SetIndexExpression( indexParser.GetIndexExpression() );
				m_expression = index;
			}
			break;
		case ETT_OpObjectMemberAccess:
			{
				this->GetTokens().MoveToNext();

				if( this->GetTokens().GetCurrentToken().type != ETT_Identifier )
				{
					EpilPointerHelper::SafeDelete( m_expression );
					throw EpilParserException( t("Identifier expected."), this->GetTokens().GetCurrentToken().location );
				}
				tstring methodName = this->GetTokens().GetCurrentToken().text;
				this->GetTokens().MoveToNext();

				EpilFunctionArgumentsParser funcArgsParser( this->GetTokens() );
				try
				{
					funcArgsParser.Parse();
				}
				catch( ... )
				{
					EpilPointerHelper::SafeDelete( m_expression );
					throw;
				}

				EpilMethodCallExpressionCode * method = new EpilMethodCallExpressionCode( m_expression->GetLocation() );
				method->SetTableExpression( m_expression );
				method->SetMethodName( methodName );
				method->SetArguments( funcArgsParser.GetArguments() );
				m_expression = method;
			}
			break;
		case ETT_OpenParenthese:
		case ETT_String:
		case ETT_OpenBrace:
			{
				EpilFunctionArgumentsParser funcArgsParser( this->GetTokens() );
				try
				{
					funcArgsParser.Parse();
				}
				catch( ... )
				{
					EpilPointerHelper::SafeDelete( m_expression );
					throw;
				}

				EpilFunctionCallExpressionCode * function = new EpilFunctionCallExpressionCode( m_expression->GetLocation() );
				function->SetTableExpression( m_expression );
				function->SetArguments( funcArgsParser.GetArguments() );
				m_expression = function;
			}
			break;
		default:
			return;
		}
	}
}